Filedialog Trick, INI File, EOF(), Run TKN, and Run Helpfile

Eddie's Lessons, version 6

level: beginner

by Alyce Watson [http://alycesrestaurant.com/]

NL136 Home

::::::::::::::::::::::::::::::::::::::::::::::::::::

Eddie Version 6

Comalspeech.dll

Point-and-Click

Dr. Strange Text

Length of Internet File

Automatic File Updating

API Corner

Wire 1.0 on the Horizon

::::::::::::::::::::::::::::::::::::::::::::::::::::

Submission Guildlines

Newsletter Help

Index


What is Eddie?

Eddie is a new code editor written in Liberty BASIC, for Liberty BASIC. Eddie can be customized and extended to meet the needs of the individual programmer. The newest version is available here: Eddie v6


Making the Filedialogs User-Friendly

A filedialog allows the user to select the filename of a file on disk. Your program can then open the file for reading or writing. The syntax for the filedialog command is as follows:

filedialog "Title Here","template here",filename$

The title is something like "Open" or "Save As" depending on the use for the filedialog. The template is the names and types of files that will be displayed. (More on this later.) The receiver variable, called filename$ in the example above, contains the name of the file chosen by the user, or an empty string if the user cancelled.

The filedialog always opens in the DefaultDir$ unless you tell it where to start. You can do this by modifying the template. A typical template looks like this:

filedialog "Open","*.txt;*.bas",filename$

The asterisk is a wildcard character. The template in this code -- "*.txt;*.bas" -- causes all files with the extensions "txt" and "bas" to be displayed in the file box of the filedialog. You can use as many of these in the template as you like. Separate the items in the list with semicolons. To open files of all types, the template looks like this: "*.*".

A Path for the Filedialog

You can cause the filedialog to open in any folder, just by altering the template string. To cause it to open in "c:\" do this:

filedialog "Open","c:\",filename$

To cause it to open in "c:\" and show only text files:

filedialog "Open","c:\*.txt",filename$

To cause it to open in "c:\" in the folder "My Progs" and to look for BAS files:

filedialog "Open","c:\myprogs\*.bas",filename$

You can use variables in the template:

folder$ = "Program Files\"
driveLetter$ = "c:\"
template$ = "*.*"
filedialog "Open",driveLetter$ + folder$ + template$,filename$

Did you notice that the backslash characters are needed? Try the code, but remove the backslash characters in the variables and see what happens.

To make the filedialog in Eddie more user-friendly, we'll keep track of the most recent directory chosen by the user, and add it to the template for all filedialogs. That way, the user won't have to navigate all over his drive and directory structure to get back to his favorite folder each time he wants to open or save a file. To accomplish this, we need to separate the path from the filename each time the user accesses a filedialog. We'll store the path in a variable called "Curdir$" to use in the template for filedialogs.

Extracting the Path from a Filename

To get the path from a complete filename, we'll use the following function, called "SeparatePath$." Here's how it works. It loops through the filename string, starting at the last character and moving forward. Using the MID$() string function, it evaluates each character in turn. If the character is not a backslash, it continues to the next character. The position in the filename is kept in the variable "fileindex" and it is decremented each time through the loop. When it reaches a character that evaluates to a backslash, it stops and exits the loop. The "fileindex" variable now holds the location of the backslash. The function returns the leftmost characters, with a length equal to the value of "fileindex".

Function SeparatePath$(f$)
    fileindex=Len(f$)
    filelength=Len(f$)
      While Mid$(f$, fileindex,1)<>"\"
        fileindex=fileindex-1
      Wend
    SeparatePath$=Left$(f$,fileindex)
End Function

Adding the Current Directory to Filedialogs

So far, we know how to cause a filedialog to open in a specified directory, and we know how to extract the path from a complete filename. Now we must put these two things together in Eddie. First, let's initialize the "Curdir$" variable at the start of the program.

Curdir$ = DefaultDir$ 

Now, each time we use a filedialog, we use it as part of the template.

    filedialog "Save As",Curdir$ + "\*.bas",savefile$

Everytime the user selects a filename with a filedialog, we update the contents of the Curdir$ variable.

    Curdir$ = SeparatePath$(savefile$)

Eddie's users will be very happy now!

Adding to the INI File

In Eddie, we can run liberty.exe for the user, but we also want to run the tokenized version of Freeform with run402.exe, and we want to be able to open the Liberty BASIC helpfile. We don't want to ask the user for these paths each time, so we'll ask him once, and store the information in the INI file, along with the path to Liberty BASIC. We'll store each bit of information on its own line. Here's how we'll write the data to a file:

[writeIniFile]
    open IniFile$ for output as #f
    print #f, LibertyExe$  'path to liberty.exe
    print #f, Freeform$    'path to freeform.tkn
    print #f, Runtime$     'path to run402.exe
    print #f, Help$        'path to liberty.hlp
    close #f
    RETURN

EOF, or "How to avoid crashing Eddie!"

We discussed how to check for the INI file existence, and prompt the user to specify the path to liberty.exe in a previous issue. We use the same methods to get the paths to the runtime engine, Freeform, and the helpfile, and we write all of this information into the INI file.

We need to read the INI file each time that Eddie opens. It isn't enough to check for the file's existence any more. If we try to read past the end of the file, the program will halt with an error. This happens when we try to INPUT from a file when there is no more data. To catch this error, we check to see if we've reached the end of the file before each INPUT statement. We do this with the EOF(#file) function. The function returns "0" if we haven't yet reached the end of the file. Once we've determined that the file length is greater than 0, we can read the first bit of data. We discussed this in a previous issue. The first line in the INI file contains the path to Liberty BASIC. After we retrieve this information, we check to see if we've reached the end of the file. If we haven't, we INPUT the next bit of data, and store it in the "Freeform$" variable:

if eof(#f) = 0 then line input #f, Freeform$

We do the same check for end of file before attempting to INPUT the path to the runtime engine, and then the path to the helpfile.

[readIniFile]
    timer 0
    open IniFile$ for append as #f
    lenFile = LOF(#f)
    close #f

    'if the length = 0, the file didn't exist, so get info from user
    if lenFile=0 then    
        notice "To use Eddie successfully, you must set the path to Liberty.exe, Freeform, the runtime engine and the helpfile."
        gosub [GetLibertyPath]
        gosub [GetFreeformPath]
        gosub [GetRuntimePath]
        gosub [GetHelpPath]
    else
        'if the length is greater than 0, the file exists
        'open it for input and read the first line into LibertyExe$
        open IniFile$ for input as #f
        line input #f, LibertyExe$
        'read second line into Freeform$, only if end of file not reached
        if eof(#f) = 0 then line input #f, Freeform$
        'read third line into Runtime$, only if end of file not reached
        if eof(#f) = 0 then line input #f, Runtime$
        'read fourth line into Help$, only if end of file not reached
        if eof(#f) = 0 then line input #f, Help$
        close #f
    end if
    TIMER 50, [activateTimer]
    RETURN

Getting the Paths from the User

Each of the subs to get a path to the desired version of Liberty BASIC, Freeform, etc. looks like this one:

[GetLibertyPath]
    timer 0
    filedialog "Find Liberty.exe",Curdir$ + "\*liberty*.exe",LibertyExe$
    if LibertyExe$="" then
        notice "Liberty.exe not found."
    else
        Curdir$ = SeparatePath$(LibertyExe$)
    end if
    TIMER 50, [activateTimer]
    RETURN

A filedialog allows the user to select the path. It includes the Curdir$ as part of the filename template. If the user cancels the dialog, he is notified that Eddie can't find the file. If the user selects a file, the Curdir$ variable is updated for its next use.

When the program closes, we save the path information to the ini file:

    
[writeIniFile]
    open IniFile$ for output as #f
    print #f, LibertyExe$
    print #f, Freeform$
    print #f, Runtime$
    print #f, Help$
    close #f
    RETURN

Running Freeform - Using Runxx.Exe

Tokenized files that were created with the GOLD version of Liberty BASIC can be run using the runtime engine. We've asked the user to specify the path to freeform and to the runtime engine, and we can now use the RUN command to run the Freeform TKN.

Here is the routine to run Freeform. We check to see if the user has specified a path to both Freeform.TKN and to the runtime engine. If both paths exist, we use the RUN command to run Freeform with the runtime engine:

[freeform]
    if Freeform$ = "" then gosub [GetFreeformPath]
    if Freeform$ = "" then wait
    if Runtime$ = "" then gosub [GetRuntimePath]
    if Runtime$="" then wait
    run Runtime$ + " " + chr$(34) + Freeform$ + chr$(34)
    wait

Running a Windows Helpfile (*.hlp)

For Eddie to be really useful, it should allow the user to access the Liberty BASIC helpfile with a simple mouse click. In the code above, we've asked the user to specify the path to the helpfile. Now, all we have to do is run it for him! It couldn't be easier! It requires the use of the RUN command, which was discussed at length in a previous version of Eddie and in other newsletter articles. We need to know which application to call in order to run a helpfile, and the required application is "winhlp32.exe." Here's the code to run the helpfile:

    run "winhlp32 " + chr$(34) + Help$ + chr$(34)

Eddie v6

Here is the code for Eddie v6.

'** Eddie - an LB Code Editor
    'version 6
    'expands ini file, reads at startup, writes at close
    'check for EOF when reading ini file with line input
    'adds menu to run user's favorite version of Freeform
    'adds runtime engine and freeform.tkn path to ini file
    'invokes LB help from help menu, adds help path to ini file
    'runs LB help with winhlp32
    'separates path from filename and stores in Curdir$
    'keep Curdir$ variable, use in filedialog, renew with each filedialog
    
if val(Version$)<4.02 then
    notice "For Liberty BASIC v4.02 and higher."
    end
end if

[VariableSetup]
    '#f will be the handle for opening files
    'note that Version$ is a reserved variable name
    EddieVersion$ = "6"
    LibertyExe$ = ""    'path to Liberty.exe for running programs:
    Freeform$ = ""      'path to Freeform
    Runtime$ = ""       'path to runtime engine, run402.exe
    Help$ = ""          'path to LB help
    IniFile$ = "eddie6.ini" 'name of ini file for this version of Eddie
    Curdir$ = DefaultDir$ 
    text$ = ""          'variable to hold contents of texteditor
    
    tempfile$ = DefaultDir$ + "\tempfile.bas" 'filename for writing a temp file to disk
    savefile$ = ""      'filename for user saving file to disk

    dim branch$(1000)       'array to hold branch labels
    branch$(1) = ""  'first designation takes you to top of code

    gosub [readIniFile]
    
[WindowSetup]
    NOMAINWIN
    WindowWidth = 600 : WindowHeight = 360
    UpperLeftX = INT((DisplayWidth-WindowWidth)/2)
    UpperLeftY = INT((DisplayHeight-WindowHeight)/2)

[ControlSetup]
'menus
Menu        #1, "&File", "&New", [new], "&Open", [open], "&Save", [save],_
            |,"&Print", [print], "E&xit", [quit]

'LB will place Edit menu where you specify in list of menus
'do not use ampersand & in Edit menu caption
Menu        #1, "Edit"
Menu        #1, "&Run","R&un", [run]
Menu        #1, "&Tools", "&Go to Line", [gotoLine], "&Freeform", [freeform]
Menu        #1, "&Help", "He&lp", [help], "&About", [about]

'buttons for a toolbar lookalike
button      #1.new, "New",[new],UL, 0, 0, 45, 20
button      #1.open, "Open",[open],UL, 45, 0, 45, 20
button      #1.save, "Save",[save],UL, 90, 0, 45, 20
button      #1.print, "Print",[print],UL, 135, 0, 45, 20
button      #1.cut, "Cut",[cut],UL, 190, 0, 45, 20
button      #1.copy, "Copy",[copy],UL, 235, 0, 45, 20
button      #1.paste, "Paste",[paste],UL, 280, 0, 45, 20
button      #1.run, "Run",[run],UL, 335, 0, 45, 20
button      #1.help, "Help",[help],UL, 380, 0, 45, 20

'a graphicbox for line numbers
stylebits #1.g, 0,_WS_BORDER,0,0
graphicbox  #1.g, 0, 52, 39, 230
'a texteditor
texteditor  #1.te, 39, 52, 545, 250
'combobox for branch labels
stylebits #1.c, 0,_CBS_AUTOHSCROLL,0,0
combobox #1.c, branch$(),[chooseBranch],40,22,385,300

Open "Eddie - an LB Code Editor v." + EddieVersion$ for Window as #1
    'trap the close event, if the user closes the
    'window with the X button
    #1 "trapclose [quit]"

    'set the font for the window and controls
    #1 "font ms_sans_serif 10"

    'set a custom font for the texteditor
    #1.te "!font courier_new 0 16"
    'set up the graphicbox
    #1.g "down; fill Buttonface; backcolor buttonface; flush"
    'use the same font as the texteditor
    #1.g "font courier_new 0 16"
    #1.g "autoresize"

    'cause the texteditor to resize automatically
    'when the user resizes the window
    #1.te "!autoresize"

    'select item in combobox
    #1.c "selectindex 1"

    TIMER 50, [activateTimer]    'initialize the timer

[loop] Wait

[quit]
    gosub [writeIniFile]
    TIMER 0
    close #1 : END

[activateTimer]
    'check the number of lines of text
    #1.te "!lines rowcount"
    'if it has changed, refill branch label combobox
    if rowcount<>lastRowcount then gosub [fillBranch]
    'new lastRowcount is this rowcount
    lastRowcount=rowcount

    'check the number of the row at the top of the texteditor
     #1.te "!origin? col row"
     'if it is the same as last check, do nothing
     if row = lastRow then wait
    'turn off timer for this routine
     TIMER 0
     'new lastRow is current row, for next check
     lastRow = row
     'delete previously named drawing segment called *linenums*
     #1.g "delsegment linenums"
     'location to start drawing line numbers
     #1.g "place 2 14"
     'fill with default buttonface color to erase previous line numbers
     #1.g "fill buttonface"
     'use top row number as starting number for line numbers
     for i = row to row+100
        'using() aligns the numbers for us
         num$=using("####",i)
         #1.g "\";num$
     next
     'flush and give this segment the name *linenums*
     #1.g "flush linenums"
     'start timer again
     TIMER 50, [activateTimer]
     wait

[new] #1.te "!CLS" : Wait

[open]
    TIMER 0
    filedialog "Open",Curdir$ + "\*.bas",openfile$
    if openfile$="" then wait

    Curdir$ = SeparatePath$(openfile$)
    
    open openfile$ for input as #f
    #1.te "!contents #f"
    close #f
    gosub [fillBranch]
    TIMER 50, [activateTimer]
    Wait

[save]
    TIMER 0
    filedialog "Save As",Curdir$ + "\*.bas",savefile$
    if savefile$="" then wait

    'make sure that user had given the file a *bas extension
    if right$(lower$(savefile$),4)<>".bas" then
        savefile$=savefile$+".bas"
    end if

    #1.te "!contents? text$"
    if text$="" then
        notice "No file to save."
        wait
    end if
    
    Curdir$ = SeparatePath$(savefile$)

    open savefile$ for output as #f
        #f text$
    close #f
    TIMER 50, [activateTimer]
    Wait

[print]
    TIMER 0
    #1.te "!contents? text$"
    if text$="" then
        notice "No text to print."
        wait
    end if

    lprint text$    'send text to printer
    dump            'cause printing to happen now
    TIMER 50, [activateTimer]
    Wait

[help]  
    if Help$ = "" then gosub [FindHelpPath]
    if Help$ = "" then wait
    run "winhlp32 " + chr$(34) + Help$ + chr$(34)
    Wait
    
[about] Wait
[cut]   #1.te "!cut" : wait
[copy]  #1.te "!copy" : wait
[paste] #1.te "!paste" : wait

[run]
    TIMER 0
    if LibertyExe$ = "" then gosub [GetLibertyPath]
    'if we can't find the path to Liberty BASIC
    'we must abort the RUN operation
    if LibertyExe$="" then wait

    #1.te "!contents? text$"
    if text$="" then
        notice "No code to run."
        wait
    end if

    open tempfile$ for output as #f
        print #f, text$
    close #f

    RUN LibertyExe$ + " " + chr$(34) + tempfile$ + chr$(34)
    TIMER 50, [activateTimer]
    wait

[freeform]
    if Freeform$ = "" then gosub [GetFreeformPath]
    if Freeform$ = "" then wait
    if Runtime$ = "" then gosub [GetRuntimePath]
    if Runtime$="" then wait
    run Runtime$ + " " + chr$(34) + Freeform$ + chr$(34)
    wait
    
[GetLibertyPath]
    timer 0
    filedialog "Find Liberty.exe",Curdir$ + "\*liberty*.exe",LibertyExe$
    if LibertyExe$="" then
        notice "Liberty.exe not found."
    else
        Curdir$ = SeparatePath$(LibertyExe$)
    end if
    TIMER 50, [activateTimer]
    RETURN

[GetRuntimePath]
    timer 0
    filedialog "Find runtime engine",Curdir$ + "\*run*.exe",Runtime$
    if Runtime$="" then
        notice "Runtime engine not found."
    else
        Curdir$ = SeparatePath$(Runtime$)    
    end if
    TIMER 50, [activateTimer]
    RETURN

[GetFreeformPath]
    timer 0
    filedialog "Find Freeform",Curdir$ + "\*.tkn",Freeform$
    if Freeform$="" then
        notice "Freeform not found."
    else
        Curdir$ = SeparatePath$(Freeform$)
    end if
    TIMER 50, [activateTimer]
    RETURN

[GetHelpPath]
    timer 0
    filedialog "Find Help",Curdir$ + "\*.hlp",Help$
    if Help$="" then
        notice "Help not found."
    else
        Curdir$ = SeparatePath$(Help$)    
    end if
    TIMER 50, [activateTimer]
    RETURN

[readIniFile]
    timer 0
    open IniFile$ for append as #f
    lenFile = LOF(#f)
    close #f

    'if the length = 0, the file didn't exist, so get info from user
    if lenFile=0 then    
        notice "To use Eddie successfully, you must set the path to Liberty.exe, Freeform, the runtime engine and the helpfile."
        gosub [GetLibertyPath]
        gosub [GetFreeformPath]
        gosub [GetRuntimePath]
        gosub [GetHelpPath]
    else
        'if the length is greater than 0, the file exists
        'open it for input and read the first line into LibertyExe$
        open IniFile$ for input as #f
        line input #f, LibertyExe$
        'read second line into Freeform$, only if end of file not reached
        if eof(#f) = 0 then line input #f, Freeform$
        'read third line into Runtime$, only if end of file not reached
        if eof(#f) = 0 then line input #f, Runtime$
        'read fourth line into Help$, only if end of file not reached
        if eof(#f) = 0 then line input #f, Help$
        close #f
    end if
    TIMER 50, [activateTimer]
    RETURN
    
[writeIniFile]
    open IniFile$ for output as #f
    print #f, LibertyExe$
    print #f, Freeform$
    print #f, Runtime$
    print #f, Help$
    close #f
    RETURN
    
[fillBranch]
    timer 0
    #1.te "!lines rowcount"
    #1.te "!origin? col row"

    REDIM branch$(rowcount)
    branch$(1)=""  + space$(300) + "1"

    'fill combobox with labels, functions and sub definitions
    'pad with 300 spaces, then add line number
    bx=2
    For i=1 to rowcount
        #1.te,"!line ";i;" line$";
            If Left$(Trim$(line$),1)="[" Then
                branch$(bx)=Trim$(line$) + space$(300) + str$(i)
                bx=bx+1
            End If
            If Lower$(Word$(line$,1))="function" Then
                branch$(bx)=Trim$(line$) + space$(300) + str$(i)
                bx=bx+1
            End If
            If Lower$(Word$(line$,1))="sub" Then
                branch$(bx)=Trim$(line$) + space$(300) + str$(i)
                bx=bx+1
            End If
    Next i

    #1.c "reload"
    #1.c "selectindex 1";
    TIMER 50, [activateTimer]
    return


[chooseBranch]
   '** CHOOSE BRANCH, SET EDITOR TO THAT POSITION
    timer 0
    #1.c "selection? branchselect$"
    
    'get the text at the end of the item
    ln$ = right$(branchselect$, 6)
    
    'get value of text, which will = linenumber
    linenumber = val(ln$)
    
    'now scroll texteditor so that line is at top
    #1.te "!origin 1 ";linenumber
    #1.te "!setfocus"
    TIMER 50, [activateTimer]
    Wait
  
[gotoLine]
    timer 0
    #1.te "!lines rowcount"
    gotoLine=rowcount
    msg$="Go to line? Max is ";gotoLine
    prompt msg$;gotoLine
    if gotoLine>rowcount then gotoLine=rowcount
    if gotoLine<1 then gotoLine=1
    #1.te "!origin 1 ";gotoLine
    #1.te "!setfocus"
    TIMER 50, [activateTimer]
    wait
                  

Function SeparatePath$(f$)
    fileindex=Len(f$)
    filelength=Len(f$)
      While Mid$(f$, fileindex,1)<>"\"
        fileindex=fileindex-1
      Wend
    SeparatePath$=Left$(f$,fileindex)
End Function
                                      


NL136 Home

::::::::::::::::::::::::::::::::::::::::::::::::::::

Eddie Version 6

Comalspeech.dll

Point-and-Click

Dr. Strange Text

Length of Internet File

Automatic File Updating

API Corner

Wire 1.0 on the Horizon

::::::::::::::::::::::::::::::::::::::::::::::::::::

Submission Guildlines

Newsletter Help

Index